# mqtt连接、重连、消息处理、发布消息
# ppp对象、wifi对象、cache对象
import paho.mqtt.client as mqtt
import time
import threading
import queue
import common_func
import logging

from common_func import print_with_location


class NanoMQClient:
    _instance = None
    _lock = threading.Lock()

    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            with cls._lock:
                if not cls._instance:
                    cls._instance = super(NanoMQClient, cls).__new__(cls)
        return cls._instance

    def __init__(
        self,
        client_id="nano_singleton",
        broker="127.0.0.1",
        port=1883,
        keepalive=60,
        sub_topics=None,
    ):
        if hasattr(self, "_initialized") and self._initialized:
            return  # 防止重复初始化

        self.topic_handlers = {}

        self.client_id = client_id
        self.broker = broker
        self.port = port
        self.keepalive = keepalive
        self.sub_topics = sub_topics or []

        self.client = mqtt.Client(client_id=str(self.client_id))
        self.client.on_connect = self.on_connect
        self.client.on_disconnect = self.on_disconnect
        self.client.on_message = self.on_message

        self.callbacks = {}
        self.lock = threading.Lock()
        self.connected = False
        self.should_run = True

        self.processing_queue = queue.Queue()  # 用于存储待处理的消息
        self.processing_thread = threading.Thread(
            target=self.process_messages, daemon=True
        )
        self.processing_thread.start()

        self._start_loop()
        self._initialized = True

    def _start_loop(self):
        threading.Thread(target=self._loop_forever, daemon=True).start()

    def register_topic_handler(self, topic, handle):
        """
        注册某个 topic 的处理函数
        """
        self.topic_handlers[topic] = handle
        print_with_location(
            f"[register_topic_handler] Registered handler for topic: {topic}"
        )

    def _loop_forever(self):
        while self.should_run:
            if not self.connected:
                try:
                    print_with_location(
                        f"[_loop_forever] Connecting to {self.broker}:{self.port}..."
                    )
                    print_with_location(
                        f"broker type: {type(self.broker)} Port type: {type(self.port)}, Keepalive type: {type(self.keepalive)}"
                    )
                    self.client.connect(
                        host=self.broker, port=self.port, keepalive=self.keepalive
                    )
                    self.client.loop_start()
                except Exception as e:
                    print_with_location(f"[_loop_forever] Connection failed: {e}")
            time.sleep(5)

    def on_connect(self, client, userdata, flags, rc):
        if rc == 0:
            with self.lock:
                self.connected = True
            print_with_location("[on_connect] Connected")
            for topic in self.sub_topics:
                self.client.subscribe(topic)
                print_with_location(f"[on_connect] Subscribed to {topic}")
        else:
            print_with_location(f"[on_connect] Connect failed: {rc}")

    def on_disconnect(self, client, userdata, rc):
        with self.lock:
            self.connected = False
        print_with_location("[on_disconnect] Disconnected")

    def on_message(self, client, userdata, msg):
        payload = msg.payload.decode()
        print_with_location(
            f"[on_message] Message received on topic {msg.topic}: {payload}"
        )
        self.processing_queue.put((msg.topic, payload))
        # 放入处理队列中

    def process_messages(self):
        while True:
            topic, payload = self.processing_queue.get()  # 获取消息
            print_with_location("[publish] topic type: {topic} payload type: {payload}")
            if self.connected:
                self.handle_message(topic, payload)
            else:
                print_with_location("[publish] Not connected")

    def publish(self, message, topic=None):
        if not self.connected:
            print_with_location("[publish] Not connected")
            return
        if topic:
            print_with_location(f"[publish] Publishing message to topic: {topic}")
            self.client.publish(topic, message)
        else:
            print_with_location("[publish] No topic specified")

        # 把状态上报

    def stop(self):
        self.should_run = False
        self.client.loop_stop()
        self.client.disconnect()

    def handle_message(self, topic, payload):
        """
        用户自定义的消息处理逻辑
        """

        handler = self.topic_handlers.get(topic)
        if handler:
            handler(payload)
        else:
            print_with_location(f"[handle_message] No handler for topic: {topic}")

    def test_topic(self):
        for topic in self.sub_topics:
            print_with_location(f"[__init__] Subscribed to {topic}")
        if self.sub_topics is None:
            print_with_location("[__init__] sub_topics is None")
